#include <android/log.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
//#include <android/binder_parcel_utils.h>
//#include <android/binder_parcelable_utils.h>

#include <unistd.h>
#include <fcntl.h>

#include <iostream>
#include <map>

#include "Logcat.h"
#include "BA330CameraManager.h"
#include "BA330CameraServiceException.h"

#include "aidl/com/ehome/ba330/IBA330CameraService.h"
#include "aidl/com/ehome/ba330/BnBA330CameraService.h"
#include "aidl/com/ehome/ba330/BpBA330CameraService.h"

#include "startup.h"

using namespace std;

void takeFluoRawPicture() {
    uint8_t params[6];
    params[0] = 0x01;
    uint32_t exposureTime = 110000000;
    memcpy(params + 1, &exposureTime, 4);
    params[5] = 20;
    int fd;
    BA330::getFile(0x07, params, 6, fd = open("/sdcard/fluo.raw", O_WRONLY | O_CREAT | O_TRUNC, 0664));
    close(fd);
}

void takeGoldRawPicture() {
    uint8_t params[6];
    params[0] = 0x00;
    uint32_t exposureTime = 1500000;
    memcpy(params + 1, &exposureTime, 4);
    params[5] = 20;
    int fd;
    BA330::getFile(0x07, params, 6, fd = open("/sdcard/gold.raw", O_WRONLY | O_CREAT | O_TRUNC, 0664));
    close(fd);
}

void takeYuvPicture() {
    uint8_t params[6];
    params[0] = 0x00;
    uint32_t exposureTime = 1500000;
    memcpy(params + 1, &exposureTime, 4);
    params[5] = 20;

    int fd;
    BA330::getFile(0x08, params, 6, fd = open("/sdcard/qrcode.yuv", O_WRONLY | O_CREAT | O_TRUNC, 0664));
    close(fd);
}


void takeFluoSequencePicture() {
    int32_t exposureSequence[] = {15000,
                                  22000,
                                  33000,
                                  50000,
                                  75000,
                                  113000,
                                  170000,
                                  256000,
                                  384000,
                                  576000,
                                  864000,
                                  1297000,
                                  1946000,
                                  2919000,
                                  4378000,
                                  6568000,
                                  9852000,
                                  14778000,
                                  22168000,
                                  33252000,
                                  49878000,
                                  74818000,
                                  112227000,
                                  112227001,
    };

    uint8_t count = 24;
    string targetArray[count];
    int fdArray[count];
    for (int i = 0; i < count; ++i) {
        char buf[1024];
        sprintf(buf, "/sdcard/%d_fluo_sequence.raw", exposureSequence[i]);
        targetArray[i].assign(buf);
        fdArray[i] = open(targetArray[i].c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0664);
    }

    BA330::takeFluoSequencePicture(exposureSequence, count, fdArray, count, 20);

    for (int i = 0; i < count; ++i) {
        close(fdArray[i]);
    }
}

void getLogFile() {
    uint8_t params[1];
    params[0] = 0x00;
    int fd;
    BA330::getFile(0x04, params, 1, fd = open("/sdcard/ba330.log", O_WRONLY | O_CREAT | O_TRUNC, 0664));
    close(fd);
}

void *getKitSateLoop(void *) {
    while (true) {
        std::cout << "getKitSate" << std::endl;
        BA330::getKitSate();
        sleep(1);
    }
}

using namespace aidl::com::ehome::ba330;

class BA330CameraService : public BnBA330CameraService {
public:
    ::ndk::ScopedAStatus getDeviceInfo(std::string *_aidl_return) override {
        char *deviceInfo = nullptr;
        try {
            BA330::getDeviceInfo(&deviceInfo);
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }

        _aidl_return->assign(deviceInfo);
        if (deviceInfo != nullptr) {
            free(deviceInfo);
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus setSerialNumber(const std::string &in_serialNumber) override {
        try {
            BA330::setSerialNumber(in_serialNumber.c_str());
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }

        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus setFile(int8_t in_fileType, ::ndk::ScopedFileDescriptor *in_source) override {
        try {
            BA330::setFile(in_fileType, in_source->get());
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus
    getFile(int8_t in_fileType, const std::vector<uint8_t> &in_params,
            ::ndk::ScopedFileDescriptor *in_target) override {
        try {
            BA330::getFile(in_fileType, in_params.data(), in_params.size(), in_target->get());
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus getKitState(int32_t *_aidl_return) override {
        try {
            *_aidl_return = BA330::getKitSate();
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus takeFluoSequencePicture(const std::vector<int32_t> &in_exposureTimeArray,
                                                 std::vector<::ndk::ScopedFileDescriptor> *in_targetArray,
                                                 int8_t in_brightness) override {
        try {
            int targetFdArray[in_targetArray->size()];
            for (int i = 0; i < in_targetArray->size(); ++i) {
                targetFdArray[i] = (*in_targetArray)[i].get();
            }
            BA330::takeFluoSequencePicture(in_exposureTimeArray.data(), in_exposureTimeArray.size(),
                                           targetFdArray, in_targetArray->size(), in_brightness);
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus controlCommand(int8_t in_command, const std::vector<uint8_t> &in_data) override {
        try {
            BA330::controlCommand(in_command, in_data.data(), in_data.size());
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus reboot() override {
        try {
            BA330::reboot();
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }

    ::ndk::ScopedAStatus setTimeStamp() override {
        try {
            BA330::setTimestamp();
        } catch (BA330ConnectionException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_CONNECTION_EXCEPTION, e.what());
        } catch (BA330CommunicationException &e) {
            return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(BA330_COMMUNICATION_EXCEPTION, e.what());
        }
        return ::ndk::ScopedAStatus::ok();
    }
};

int main(int argc, char **argv) {
    LOGI("%s start ...", TAG);
    if (argc == 1) {
        //server
        startup();

        //client
//    AIBinder* aiBinder = AServiceManager_getService("ba330");
//    const ::ndk::SpAIBinder sp(aiBinder);
//    std::shared_ptr<IBA330CameraService> service = BpBA330CameraService::fromBinder(sp);
//    service->test(1);

    } else {
        int fd;
        uint64_t count = 0;
//    pthread_t pthread;
//    pthread_create(&pthread, nullptr, getKitSateLoop, nullptr);

        while (true) {
            count++;
            std::cout << "第" << count << "次" << std::endl;
//            LOGD("setSerialNumber");
//            std::cout << "setSerialNumber" << std::endl;
//            BA330::setSerialNumber("WEF100125041800001");
            std::cout << "getDeviceInfo" << std::endl;
            char *deviceInfo = nullptr;
            BA330::getDeviceInfo(&deviceInfo);
            if (deviceInfo != nullptr) {
                free(deviceInfo);
            }
//            LOGD("setCalibrationFiles");
//            std::cout << "setCalibrationFiles" << std::endl;
//
//            BA330::setFile(MESSAGE_ID_SET_CALIBRATION_FILE, fd = open("/sdcard/test.zip", O_RDONLY));
//            close(fd);
//            LOGD("getCalibrationFiles");
//            std::cout << "getCalibrationFiles" << std::endl;
//            BA330::getFile(MESSAGE_ID_GET_CALIBRATION_FILE, nullptr, 0,
//                           fd = open("/sdcard/Calibration.zip", O_WRONLY | O_CREAT | O_TRUNC, 0664));
//            close(fd);
            LOGD("takeYuvPicture");
            std::cout << "takeYuvPicture" << std::endl;
            takeYuvPicture();
            LOGD("takeGoldRawPicture");
            std::cout << "takeGoldRawPicture" << std::endl;
            takeGoldRawPicture();
            LOGD("takeFluoSequencePicture");
            std::cout << "takeFluoSequencePicture" << std::endl;
            takeFluoSequencePicture();
            LOGD("takeFluoRawPicture");
            std::cout << "takeFluoRawPicture" << std::endl;
            takeFluoRawPicture();
            LOGD("getLogFile");
            std::cout << "getLogFile" << std::endl;
            getLogFile();
        }
    }

    return 0;
}


void startup() {
    ABinderProcess_startThreadPool();

    //server
    std::shared_ptr<BA330CameraService> spBinder = ndk::SharedRefBase::make<BA330CameraService>();
    AServiceManager_addService(spBinder->asBinder().get(), "ba330");

    ABinderProcess_joinThreadPool();
}